﻿#region 文件信息

/*----------------------------------------------------------------
//
// 文件名称：
// 文件功能描述：
// 设计要求：
//
// 文 件 名：    FlowDesign
// 创建者：      杨程
// 创建日期：	    2023/1/13 13:53:30

//----------------------------------------------------------------*/

#endregion

using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using Vampirewal.Admin.FlowEngine.Models;

namespace Vampirewal.Admin.FlowEngineDesign;

/// <summary>
///
/// </summary>
public partial class FlowDesign : UserControl
{
    private ResourceDictionary res
    {
        get
        {
            return new ResourceDictionary() { Source = new Uri("pack://application:,,,/Vampirewal.Admin.FlowEngineDesign;component/Design/FlowDesignStyles.xaml", UriKind.RelativeOrAbsolute) };
        }
    }

    static FlowDesign()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(FlowDesign), new FrameworkPropertyMetadata(typeof(FlowDesign)));
    }

    private Canvas canvas { get; set; }

    /// <summary>
    ///
    /// </summary>
    public FlowDesign()
    {
        //构造函数
        var BaseStyle = res["Design"] as Style;

        this.Style = BaseStyle;
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        #region 查找界面控件

        var StartNode = this.Template.FindName("StartNode", this) as Border;
        var ToDoNode = this.Template.FindName("ToDoNode", this) as Border;
        var EndNode = this.Template.FindName("EndNode", this) as Border;
        canvas = this.Template.FindName("canvas", this) as Canvas;

        RestButton = this.Template.FindName("RestButton", this) as Button;
        SaveButton = this.Template.FindName("SaveButton", this) as Button;

        #endregion

        StartNode.MouseLeftButtonDown += StartNode_MouseLeftButtonDown;
        ToDoNode.MouseLeftButtonDown += StartNode_MouseLeftButtonDown;
        EndNode.MouseLeftButtonDown += StartNode_MouseLeftButtonDown;

        canvas.Drop += Canvas_Drop;

        canvas.SizeChanged += Canvas_SizeChanged;

        RestButton.Click += RestButton_Click;
        SaveButton.Click += SaveButton_Click;
    }

    /// <summary>
    /// 保存流程按钮点击事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void SaveButton_Click(object sender, RoutedEventArgs e)
    {
        if (SaveFlowCommand == null)
        {
            throw new Exception("未绑定保存的Command命令!");
        }

        foreach (FlowStep step in FlowNodeBuilder)
        {
            var node = FlowNodeList.FirstOrDefault(f => f.DtlId == step.DtlId);

            if (node == null)
            {
                continue;
            }

            step.NodeName = node.NodeName;
            step.NodePositionX = node.NodePositionX;
            step.NodePositionY = node.NodePositionY;
            step.NodeType = node.NodeType;
            step.FlowNodeNo = node.FlowNodeNo;
            step.UpNodeId = node.UpNode != null ? node.UpNode.DtlId : "";
            step.DownNodeId = node.DownNode != null ? node.DownNode.DtlId : "";
            step.RejectNodeId = node.RejectNode != null ? node.RejectNode.DtlId : "";
            step.ParentId = node.ParentId;
        }

        SaveFlowCommand.Execute(FlowNodeBuilder.ToList());
    }

    /// <summary>
    /// 底部重置按钮的点击事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void RestButton_Click(object sender, RoutedEventArgs e)
    {
        canvas.Children.Clear();
        NodeName?.Clear();
        FlowNodeList?.Clear();
    }

    #region 界面控件

    private Button RestButton { get; set; }
    private Button SaveButton { get; set; }

    #endregion

    private void Canvas_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        Canvas canvas = (Canvas)sender;
        //canvas.Width = e.;
        //canvas.Height = canvas.ActualHeight;
    }

    private List<FlowNode> FlowNodeList { get; set; } = new List<FlowNode>();

    private void Canvas_Drop(object sender, DragEventArgs e)
    {
        var data = e.Data.GetData(typeof(string));

        if (NodeName.Any(a => a.Equals(data.ToString())) && (data.ToString() == "开始" || data.ToString() == "结束"))
        {
            return;
        }

        Canvas canvas = (Canvas)sender;

        FlowNode node = new FlowNode();
        node.DtlId = Guid.NewGuid().ToString();
        node.Cursor = System.Windows.Input.Cursors.Hand;
        if (data.ToString() == "开始")
        {
            node.NodeType = 1;
        }
        else if (data.ToString() == "执行中")
        {
            node.NodeType = 2;
        }
        else if (data.ToString() == "结束")
        {
            node.NodeType = 99;
        }

        node.FlowNodeNo = $"Node-{FlowNodeList.Count + 1}";

        node.NodeName = data.ToString();

        CreateNode(node, e.GetPosition(canvas));
    }

    /// <summary>
    /// 创建节点到canvas上
    /// </summary>
    /// <param name="node"></param>
    /// <param name="Position"></param>
    private void CreateNode(FlowNode node, Point Position)
    {
        node.DragDelta += Node_DragDelta;
        node.DragStarted += Node_DragStarted;
        //node.MouseRightButtonDown += Node_MouseRightButtonDown;//节点点击右键，在设计界面右侧出现设置界面

        #region 在节点上添加右键上下文菜单

        ContextMenu cm = new ContextMenu();

        MenuItem LookMenu = new MenuItem()
        {
            Header = "查看节点信息",
            Tag = node
        };
        LookMenu.Click += LookMenu_Click;
        cm.Items.Add(LookMenu);

        Separator separator1 = new Separator();
        cm.Items.Add(separator1);

        MenuItem AddUpNode = new MenuItem()
        {
            Header = "添加为上级节点",
            Tag = node
        };
        AddUpNode.Click += AddUpNode_Click;
        cm.Items.Add(AddUpNode);

        MenuItem AddDownNode = new MenuItem()
        {
            Header = "添加为下级节点",
            Tag = node
        };
        AddDownNode.Click += AddDownNode_Click;
        cm.Items.Add(AddDownNode);

        Separator separator2 = new Separator();
        cm.Items.Add(separator2);

        MenuItem AddRejectNode = new MenuItem()
        {
            Header = "添加为回退节点",
            Tag = node
        };
        AddRejectNode.Click += AddRejectNode_Click;
        cm.Items.Add(AddRejectNode);

        node.ContextMenu = cm;

        #endregion

        Canvas.SetTop(node, Position.Y);
        Canvas.SetLeft(node, Position.X);

        node.NodePositionX = Position.X;
        node.NodePositionY = Position.Y;

        Canvas.SetZIndex(node, 99);
        canvas.Children.Add(node);
        if (!FlowNodeList.Any(a=>a.DtlId==node.DtlId))
        {
            FlowNodeList.Add(node);
        }
        
        NodeName.Add(node.NodeName);
        if (!FlowNodeBuilder.Any(a => a.DtlId.Equals(node.DtlId)))
        {
            FlowStep step = new FlowStep()
            {
                BillId = node.BillId,
                DtlId = node.DtlId,
                DownNodeId = node.DownNode != null ? node.DownNode.BillId : "",
                ParentId = node.ParentId,
                FlowNodeNo = node.FlowNodeNo,
                NodeName = node.NodeName,
                NodePositionX = node.NodePositionX,
                NodePositionY = node.NodePositionY,
                NodeType = node.NodeType,
                RejectNodeId = node.RejectNode != null ? node.RejectNode.BillId : "",
                UpNodeId = node.UpNode != null ? node.UpNode.BillId : ""
            };

            FlowNodeBuilder.Add(step);
        }
    }

    private void AddRejectNode_Click(object sender, RoutedEventArgs e)
    {
        FlowNode node = (FlowNode)((MenuItem)sender).Tag;

        var child = FlowNodeList.Find(f => f.ParentId == FlowNodeInfo.DtlId);

        FlowNode ErrorNode = null;

        while (child != null)
        {
            if (child.DtlId == node.DtlId)
            {
                ErrorNode = child;
                break;
            }

            child = FlowNodeList.Find(f => f.ParentId == child.DtlId);
        }

        if (ErrorNode == null)
        {
            FlowNodeInfo.RejectNode = node;
        }
    }

    /// <summary>
    /// 添加为下级节点
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void AddDownNode_Click(object sender, RoutedEventArgs e)
    {
        FlowNode node = (FlowNode)((MenuItem)sender).Tag;

        if (FlowNodeInfo.NodeType != 99)
        {
            FlowNodeInfo.DownNode = node;
            var CurUp= FlowNodeList.FirstOrDefault(f => f.DtlId == FlowNodeInfo.DtlId);
            CurUp.DownNode = node;

            var CurUpBuilder= FlowNodeBuilder.FirstOrDefault(f => f.DtlId == FlowNodeInfo.DtlId);
            CurUpBuilder.DownNodeId = node.DtlId;

            node.UpNode = FlowNodeInfo;
            node.ParentId = FlowNodeInfo.DtlId;

            var CurNode= FlowNodeList.FirstOrDefault(f=>f.DtlId==node.DtlId);
            CurNode.UpNode= FlowNodeInfo;
            CurNode.ParentId= FlowNodeInfo.DtlId;

            var CurBuilder = FlowNodeBuilder.FirstOrDefault(f => f.DtlId == FlowNodeInfo.DtlId);
            CurBuilder.UpNodeId = FlowNodeInfo.DtlId;
            CurBuilder.ParentId = FlowNodeInfo.DtlId;

            UpdateThumb(FlowNodeInfo, node);
        }
    }

    /// <summary>
    /// 添加为上级节点
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    /// <exception cref="NotImplementedException"></exception>
    private void AddUpNode_Click(object sender, RoutedEventArgs e)
    {
        FlowNode node = (FlowNode)((MenuItem)sender).Tag;

        if (FlowNodeInfo.NodeType != 1)
        {
            FlowNodeInfo.UpNode = node;
            FlowNodeInfo.ParentId = node.DtlId;
            var CurUp = FlowNodeList.FirstOrDefault(f => f.DtlId == FlowNodeInfo.DtlId);
            CurUp.DownNode = node;
            CurUp.ParentId= node.DtlId;

            var CurUpBuilder = FlowNodeBuilder.FirstOrDefault(f => f.DtlId == FlowNodeInfo.DtlId);
            CurUpBuilder.DownNodeId = node.DtlId;
            CurUpBuilder.ParentId = node.DtlId;

            node.DownNode = FlowNodeInfo;
            var CurNode = FlowNodeList.FirstOrDefault(f => f.DtlId == node.DtlId);
            CurNode.DownNode = FlowNodeInfo;

            var CurBuilder = FlowNodeBuilder.FirstOrDefault(f => f.DtlId == node.DtlId);
            CurBuilder.DownNodeId = FlowNodeInfo.DtlId;

            UpdateThumb(node, FlowNodeInfo);
        }
    }

    /// <summary>
    /// 界面上鼠标右键点击节点的事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void LookMenu_Click(object sender, RoutedEventArgs e)
    {
        FlowNode node = (FlowNode)((MenuItem)sender).Tag;
        FlowNodeInfo = node;
    }

    /// <summary>
    /// 节点拖拽开始
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Node_DragStarted(object sender, DragStartedEventArgs e)
    {
    }

    /// <summary>
    /// 节点拖拽中
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Node_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
    {
        FlowNode node = (FlowNode)sender;
        double nTop = Canvas.GetTop(node) + e.VerticalChange;
        double nLeft = Canvas.GetLeft(node) + e.HorizontalChange;
        //double nRight = Canvas.GetRight(myThumb) + e.VerticalChange;
        //double nBottom = Canvas.GetBottom(myThumb) + e.HorizontalChange;

        if (nTop < 0)
        {
            nTop = 0;
        }
        else if ((nTop + node.Height) > canvas.Height)
        {
            nTop = canvas.Height - node.ActualHeight;
        }
        else if (nTop + node.ActualHeight >= canvas.ActualHeight)
        {
            nTop = canvas.ActualHeight - node.ActualHeight;
        }

        if (nLeft < 0)
        {
            nLeft = 0;
        }
        else if ((nLeft + node.Width) > canvas.Height)
        {
            nLeft = canvas.Height - node.ActualWidth;
        }
        else if (nLeft + node.ActualWidth >= canvas.ActualWidth)
        {
            nLeft = canvas.ActualWidth - node.ActualWidth;
        }

        Canvas.SetTop(node, nTop);
        Canvas.SetLeft(node, nLeft);
        node.ToolTip = $"X:{nTop}\r\nY:{nLeft}";

        node.NodePositionX = nTop;
        node.NodePositionY = nLeft;

        if (node.DownNode != null)
        {
            UpdateThumb(node, node.DownNode, false);
        }

        if (node.UpNode != null)
        {
            UpdateThumb(node.UpNode, node, false);
        }
    }

    private List<string> NodeName = new List<string>();

    private void StartNode_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        try
        {
            Border border = (Border)sender;

            DragDrop.DoDragDrop(border, border.Tag, DragDropEffects.Copy);
        }
        catch (Exception ex)
        {
        }
    }

    #region 节点连线相关

    /// <summary>
    /// 绘制连线
    /// </summary>
    /// <param name="node1">父节点</param>
    /// <param name="node2">自己而几点</param>
    /// <param name="IsNew">是否新增</param>
    private void UpdateThumb(FlowNode node1, FlowNode node2, bool IsNew = true)
    {
        if (node2.OwnPath != null && IsNew)
        {
            return;
        }

        //var point1 = new Point((double)node1.GetValue(Canvas.LeftProperty) + node1.ActualWidth, (double)node1.GetValue(Canvas.TopProperty) + node1.ActualHeight / 2);
        //var point2 = new Point((double)node2.GetValue(Canvas.LeftProperty), (double)node2.GetValue(Canvas.TopProperty) + node2.ActualHeight / 2);

        var point1 = new Point((double)node1.GetValue(Canvas.LeftProperty) + node1.ActualWidth / 2, (double)node1.GetValue(Canvas.TopProperty) + node1.ActualHeight / 2);
        var point2 = new Point((double)node2.GetValue(Canvas.LeftProperty) + node2.ActualWidth / 2, (double)node2.GetValue(Canvas.TopProperty) + node2.ActualHeight / 2);

        if (IsNew)
        {
            System.Windows.Shapes.Path path = new System.Windows.Shapes.Path();

            string sData = "M 0,0 c 200,0 100,300 300,300";
            var converter = TypeDescriptor.GetConverter(typeof(Geometry));

            path.Data = (Geometry)converter.ConvertFrom(sData);
            path.Height = 200;
            path.Stretch = Stretch.Fill;
            path.Stroke = Brushes.Green;//可以修改颜色，后面可以将这个做成依赖属性，放到xaml中进行绑定
            path.StrokeStartLineCap = PenLineCap.Round;
            path.StrokeEndLineCap = PenLineCap.Round;
            path.StrokeThickness = 2;//线条粗细
            path.Width = 200;
            path.RenderTransformOrigin = new Point(0.5, 0.5);

            //path.RenderTransform

            ScaleTransform scale = new ScaleTransform();
            path.RenderTransform = scale;

            path.SetValue(Canvas.LeftProperty, Math.Min(point1.X, point2.X));
            path.SetValue(Canvas.TopProperty, Math.Min(point1.Y, point2.Y));
            path.Width = Math.Abs(point1.X - point2.X);
            path.Height = Math.Abs(point1.Y - point2.Y);
            if (point1.X < point2.X)
            {
                scale.ScaleX = point1.Y < point2.Y ? 1 : -1;
            }
            else
            {
                scale.ScaleX = point1.Y < point2.Y ? -1 : 1;
            }

            Canvas.SetZIndex(path, 0);
            //将线条添加进canvas中显示
            canvas.Children.Add(path);
            //将线条传入自身节点中储存
            node2.OwnPath = path;
        }
        else if (node2.OwnPath != null)
        {
            node2.OwnPath.SetValue(Canvas.LeftProperty, Math.Min(point1.X, point2.X));
            node2.OwnPath.SetValue(Canvas.TopProperty, Math.Min(point1.Y, point2.Y));
            node2.OwnPath.Width = Math.Abs(point1.X - point2.X);
            node2.OwnPath.Height = Math.Abs(point1.Y - point2.Y);

            ScaleTransform scale = new ScaleTransform();
            node2.OwnPath.RenderTransform = scale;

            if (point1.X < point2.X)
            {
                scale.ScaleX = point1.Y < point2.Y ? 1 : -1;
            }
            else
            {
                scale.ScaleX = point1.Y < point2.Y ? -1 : 1;
            }
        }
    }

    #endregion

    #region [     依赖属性     ]

    #region 查看节点属性

    public FlowNode FlowNodeInfo
    {
        get { return (FlowNode)GetValue(FlowNodeInfoProperty); }
        set { SetValue(FlowNodeInfoProperty, value); }
    }

    // Using a DependencyProperty as the backing store for FlowNodeInfo.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty FlowNodeInfoProperty =
        DependencyProperty.Register("FlowNodeInfo", typeof(FlowNode), typeof(FlowDesign), new PropertyMetadata(null));

    #endregion

    #region 底部按钮的样式

    /// <summary>
    /// 底部重置按钮的样式
    /// </summary>
    public Style RestButtonStyle
    {
        get { return (Style)GetValue(RestButtonStyleProperty); }
        set { SetValue(RestButtonStyleProperty, value); }
    }

    // Using a DependencyProperty as the backing store for RestButtonStyle.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty RestButtonStyleProperty =
        DependencyProperty.Register("RestButtonStyle", typeof(Style), typeof(FlowDesign), new PropertyMetadata(null));

    /// <summary>
    /// 底部保存按钮样式
    /// </summary>
    public Style SaveButtonStyle
    {
        get { return (Style)GetValue(SaveButtonStyleProperty); }
        set { SetValue(SaveButtonStyleProperty, value); }
    }

    // Using a DependencyProperty as the backing store for SaveButtonStyle.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SaveButtonStyleProperty =
        DependencyProperty.Register("SaveButtonStyle", typeof(Style), typeof(FlowDesign), new PropertyMetadata(null));

    #endregion

    #region 数据源

    /// <summary>
    /// 数据源
    /// </summary>
    public ObservableCollection<FlowStep> FlowNodeBuilder
    {
        get { return (ObservableCollection<FlowStep>)GetValue(FlowNodeBuilderProperty); }
        set { SetValue(FlowNodeBuilderProperty, value); }
    }

    // Using a DependencyProperty as the backing store for ItemsSource.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty FlowNodeBuilderProperty =
        DependencyProperty.Register("FlowNodeBuilder", typeof(ObservableCollection<FlowStep>), typeof(FlowDesign), new PropertyMetadata(new ObservableCollection<FlowStep>(), OnItemsSourceChangedCallBack));

    private static void OnItemsSourceChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        FlowDesign flowDesign = (FlowDesign)d;
        flowDesign.FlowDesignCollectionChanged();
    }

    //监听就在这里
    public void FlowDesignCollectionChanged()
    {
        if (FlowNodeBuilder is INotifyCollectionChanged)
        {
            (FlowNodeBuilder as INotifyCollectionChanged).CollectionChanged -= FlowDesign_CollectionChanged;
            (FlowNodeBuilder as INotifyCollectionChanged).CollectionChanged += FlowDesign_CollectionChanged;
        }
    }

    private void FlowDesign_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        try
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    {
                        foreach (var item in e.NewItems)
                        {
                            var step = item as FlowStep;

                            Point point = new Point(step.NodePositionX, step.NodePositionY);

                            FlowNode node = new FlowNode()
                            {
                                NodeType = step.NodeType,
                                BillId = step.BillId,
                                DtlId = step.DtlId,
                                ParentId = step.ParentId,
                                NodeName = step.NodeName,
                                NodePositionX = step.NodePositionX,
                                NodePositionY = step.NodePositionY,
                                FlowNodeNo = step.FlowNodeNo,
                            };

                            CreateNode(node, point);
                        }
                    }
                    break;

                case NotifyCollectionChangedAction.Remove:
                    break;

                case NotifyCollectionChangedAction.Replace:
                    break;

                case NotifyCollectionChangedAction.Move:
                    break;

                case NotifyCollectionChangedAction.Reset:
                    break;

                default:
                    break;
            }
        }
        catch (Exception)
        {
            throw;
        }
    }

    #endregion

    #region 保存命令

    /// <summary>
    /// 保存命令
    /// </summary>
    public ICommand SaveFlowCommand
    {
        get { return (ICommand)GetValue(SaveFlowCommandProperty); }
        set { SetValue(SaveFlowCommandProperty, value); }
    }

    // Using a DependencyProperty as the backing store for SaveFlowCommand.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SaveFlowCommandProperty =
        DependencyProperty.Register("SaveFlowCommand", typeof(ICommand), typeof(FlowDesign), new PropertyMetadata(null));

    #endregion

    #endregion
}